package com.jeasonzhao.commons.utils;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import sun.tools.java.Environment;

public class ResourceHelper
{
    private static final String FILE_PROTOCOL = "file://";
    private ResourceHelper()
    {
        super();
    }

    public static InputStream streamFromResource(Class<?> cls,String strResourceName)
        throws ResourceException
    {
        try
        {
            if(null == strResourceName)
            {
                return null;
            }
            if(null == cls)
            {
                cls = ResourceHelper.class; //Local resource
            }
            //Is it a file?
            if(strResourceName.startsWith(FILE_PROTOCOL))
            {
                File file = new File(strResourceName.substring(FILE_PROTOCOL.length()));
                if(file.exists())
                {
                    return new java.io.FileInputStream(file);
                }
                else
                {
                    return null;
                }
            }
            InputStream stream = null;
            //If the file with the same name existing
            File file = new File(strResourceName);
            if(file.exists())
            {
                return new java.io.FileInputStream(file);
            }
            //Remove root prefix
            String strNewResouce = strResourceName.startsWith("/") ? strResourceName.substring(1) : strResourceName;
            //Load resource from my Class Loader
            if(null == stream)
            {
                ClassLoader classLoader = cls.getClassLoader();
                stream = classLoader.getResourceAsStream(strNewResouce);
            }
            //Load resource from current ClassLoader
            if(null == stream)
            {
                ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
                stream = classLoader.getResourceAsStream(strNewResouce);
            }
            //Load resource from protection domain.
            if(null == stream)
            {
                ClassLoader classLoader = cls.getProtectionDomain().getClassLoader();
                stream = classLoader.getResourceAsStream(strNewResouce);
            }
            //Load resource from Environment.
            if(null == stream)
            {
                stream = Environment.class.getResourceAsStream(strResourceName);
            }
            //Again
            if(null == stream)
            {
                stream = Environment.class.getClassLoader().getResourceAsStream(strNewResouce);
            }
            //Try again,from Jar files
            if(null == stream)
            {
                file = new java.io.File(cls.getProtectionDomain().getCodeSource().getLocation().getFile());
                if(file.isDirectory() == false)
                {
                    strResourceName = strResourceName.replaceAll("\\\\","/").replaceAll("//","/").replaceAll("//","/");
                    if(strResourceName.startsWith("/"))
                    {
                        strResourceName = strResourceName.substring(1);
                    }
                    JarFile jarFile = new JarFile(URLDecoder.decode(file.getAbsolutePath(),"utf-8"));
                    JarEntry entry = null == jarFile ? null : jarFile.getJarEntry(strResourceName);
                    if(null != entry)
                    {
                        stream = new BufferedInputStream(jarFile.getInputStream(entry));
                    }
                }
            }
            //Failed, eventually.
            if(null == stream)
            {
                throw new ResourceException("Could not found the resource named:" + strResourceName);
            }
            return stream;
        }
        catch(ResourceException excep)
        {
            throw excep;
        }
        catch(Exception excep)
        {
            throw new ResourceException("Can not found the resource named:" + strResourceName,excep);
        }
    }

    /**
     * Read whole text from a resource.
     * @param strResourceName String The name of the resource which expect to read.
     * @return String The text of the resource
     * @throws ResourceException
     */
    public static String stringFromResource(String strResourceName)
        throws ResourceException
    {
        return stringFromResource(strResourceName,false);
    }

    /**
     * Read whole text from a resource.
     * @param strResourceName String The name of the resource which expect to read.
     * @param removeLine boolean Remove all line-breaks?
     * @return String
     */
    public static String stringFromResource(String strResourceName,boolean removeLine)
    {
        java.io.BufferedReader reader = null;
        try
        {
            reader = new java.io.BufferedReader(
                new java.io.InputStreamReader(
                    ResourceHelper.streamFromResource(strResourceName))
                );
            StringBuffer ret = new StringBuffer();
            String strLine = null;
            while(null != (strLine = reader.readLine()))
            {
                ret.append(strLine);
                if(removeLine == false)
                {
                    ret.append('\n');
                }
            }
            return ret.toString();
        }
        catch(IOException ex)
        {
            ex.printStackTrace();
            return null;
        }
        catch(ResourceException ex)
        {
            ex.printStackTrace();
            return null;
        }
        finally
        {
            if(null != reader)
            {
                try
                {
                    reader.close();
                }
                catch(IOException ex1)
                {
                	ex1.printStackTrace();
                }
                reader = null;
            }
        }
    }

    public static String stringFromClassPath(Class<?> cls,String strResourceName)
    {
        if(null == cls || null == strResourceName)
        {
            return null;
        }
        return stringFromResource("/" + cls.getPackage().getName().replaceAll("\\.","/") +
                                  "/" + strResourceName,false);
    }

    public static InputStream streamFromResource(String strResourceName)
        throws ResourceException
    {
        return streamFromResource(ResourceHelper.class,strResourceName);
    }

    public static InputStream streamFromClassPath(Class<?> cls,String strResourceName)
        throws ResourceException
    {
        if(null == cls || null == strResourceName)
        {
            return null;
        }
        return streamFromResource(cls,"/" + cls.getPackage().getName().replaceAll("\\.","/") +
                                  "/" + strResourceName);
    }

    public static File file(String strResource)
    {
        return file(null,strResource);
    }

    public static File fileFromClassPath(Class<?> cls)
    {
        return fileFromClassPath(cls,""); //not null!!!
    }

    public static File fileFromClassPath(Class<?> cls,String strResourceName)
    {
        if(null == cls || null == strResourceName)
        {
            return null;
        }
        else
        {
            return file(cls,"/" + cls.getPackage().getName().replaceAll("\\.","/") + "/" + strResourceName);
        }
    }

    public static File file(Class<?> cls,String strResource)
    {
        if(null == strResource)
        {
            return null;
        }
        return getResourceFile_backyard(cls,decode(strResource));
    }

    private static File getResourceFile_backyard(Class<?> cls,String strResource)
    {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        URL ret = classLoader.getResource(strResource);
        if(null == ret || new File(ret.getFile()).exists() == false)
        {
            if(null == cls)
            {
                cls = ResourceHelper.class;
            }
            classLoader = cls.getClassLoader();
            ret = classLoader.getResource(strResource);
        }
        if(null == ret || new File(ret.getFile()).exists() == false)
        {
            ret = Environment.class.getResource(strResource);
        }
        if(null == ret || new File(ret.getFile()).exists() == false)
        {
            ret = Environment.class.getClassLoader().getResource(strResource);
        }
        if(null == ret || new File(ret.getFile()).exists() == false)
        {
            File file = new File(strResource);
            if(file.exists())
            {
                return file;
            }
            else
            {
                file = new File(getClassLocationDirectory(cls) + "/" + strResource);
                if(file.exists())
                {
                    return file;
                }
                else
                {
                    return null;
                }
            }
        }
        else
        {
            return new File(ret.getFile());
        }
    }

    public static String getClassLocationDirectory()
    {
        return getClassLocationDirectory(null);
    }

    public static String getClassLocationDirectory(Class<?> cls)
    {
        if(null == cls)
        {
            cls = ResourceHelper.class;
        }
        String strPathInit = getCodeSourceLocation(cls);
        String str1 = cls.getName().replaceAll("\\.","/") + ".class";
        String str2 = cls.getName().replaceAll("\\.","\\\\") + ".class";
        //remove ....class from path,get the root, no impact with Jar files
        if(strPathInit.endsWith(str1))
        {
            strPathInit = strPathInit.substring(0,strPathInit.length() - str1.length());
        }
        else if(strPathInit.endsWith(str2))
        {
            strPathInit = strPathInit.substring(0,strPathInit.length() - str2.length());
        }
        File file = new File(strPathInit);
        //remove ***.jar from path
        String strPath = "";
        if(file.exists() == false || file.isDirectory())
        {
            strPath = file.getAbsolutePath();
        }
        else
        {
            //Jar files
            strPath = file.getParentFile().getAbsolutePath();
        }
        return decode(strPath.replace('\\','/'));

    }

    private static final String decode(String s)
    {
        try
        {
            return null == s ? s : URLDecoder.decode(s,"utf-8");
        }
        catch(UnsupportedEncodingException ex)
        {
            return s;
        }
    }

    public static String getCodeSource(Class<?> cls)
    {
        return new java.io.File(getCodeSourceLocation(cls)).getAbsolutePath().replace('\\','/');
    }

    private static String getCodeSourceLocation(Class<?> cls)
    {
        if(null == cls)
        {
            cls = ResourceHelper.class;
        }
        return decode(cls.getProtectionDomain().getCodeSource().getLocation().getFile());
    }

    public static void main(String[] argvs)
    {
        System.out.println(decode("AAABB"));
        System.out.println(decode("A%AABB中文"));
    }
}
